/*******************************************************************************
 * Copyright (c) 2012-2013,2015 University of Stuttgart.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and the Apache License 2.0 which both accompany this distribution,
 * and are available at http://www.eclipse.org/legal/epl-v10.html
 * and http://www.apache.org/licenses/LICENSE-2.0
 *
 * Contributors:
 *     K��lm��n K��pes - initial API and implementation and/or initial documentation
 *     Oliver Kopp - adapted to new storage model and to TOSCA v1.0
 *******************************************************************************/
/*
 * Modifications Copyright 2016 ZTE Corporation.
 */

package org.eclipse.winery.repository.importing;

import static java.nio.file.FileVisitResult.CONTINUE;
import static java.nio.file.FileVisitResult.SKIP_SUBTREE;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import javax.ws.rs.core.MediaType;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.io.FilenameUtils;
import org.eclipse.winery.common.ModelUtilities;
import org.eclipse.winery.common.RepositoryFileReference;
import org.eclipse.winery.common.Util;
import org.eclipse.winery.common.constants.MimeTypes;
import org.eclipse.winery.common.constants.Namespaces;
import org.eclipse.winery.common.ids.XMLId;
import org.eclipse.winery.common.ids.definitions.ArtifactTemplateId;
import org.eclipse.winery.common.ids.definitions.EntityTypeId;
import org.eclipse.winery.common.ids.definitions.NodeTypeId;
import org.eclipse.winery.common.ids.definitions.RelationshipTypeId;
import org.eclipse.winery.common.ids.definitions.ServiceTemplateId;
import org.eclipse.winery.common.ids.definitions.TOSCAComponentId;
import org.eclipse.winery.common.ids.definitions.imports.GenericImportId;
import org.eclipse.winery.common.ids.definitions.imports.XSDImportId;
import org.eclipse.winery.common.ids.elements.PlanId;
import org.eclipse.winery.common.ids.elements.PlansId;
import org.eclipse.winery.common.propertydefinitionkv.WinerysPropertiesDefinition;
import org.eclipse.winery.model.csar.toscametafile.TOSCAMetaFile;
import org.eclipse.winery.model.csar.toscametafile.TOSCAMetaFileParser;
import org.eclipse.winery.model.tosca.Definitions;
import org.eclipse.winery.model.tosca.TArtifactReference;
import org.eclipse.winery.model.tosca.TArtifactReference.Exclude;
import org.eclipse.winery.model.tosca.TArtifactReference.Include;
import org.eclipse.winery.model.tosca.TArtifactTemplate;
import org.eclipse.winery.model.tosca.TArtifactTemplate.ArtifactReferences;
import org.eclipse.winery.model.tosca.TDefinitions;
import org.eclipse.winery.model.tosca.TDefinitions.Types;
import org.eclipse.winery.model.tosca.TEntityType;
import org.eclipse.winery.model.tosca.TEntityType.PropertiesDefinition;
import org.eclipse.winery.model.tosca.TExtensibleElements;
import org.eclipse.winery.model.tosca.TImport;
import org.eclipse.winery.model.tosca.TNodeType;
import org.eclipse.winery.model.tosca.TPlan;
import org.eclipse.winery.model.tosca.TPlan.PlanModelReference;
import org.eclipse.winery.model.tosca.TPlans;
import org.eclipse.winery.model.tosca.TRelationshipType;
import org.eclipse.winery.model.tosca.TServiceTemplate;
import org.eclipse.winery.repository.Constants;
import org.eclipse.winery.repository.JAXBSupport;
import org.eclipse.winery.repository.Utils;
import org.eclipse.winery.repository.backend.BackendUtils;
import org.eclipse.winery.repository.backend.Repository;
import org.eclipse.winery.repository.backend.constants.Filename;
import org.eclipse.winery.repository.backend.filebased.FileUtils;
import org.eclipse.winery.repository.datatypes.ids.elements.ArtifactTemplateDirectoryId;
import org.eclipse.winery.repository.datatypes.ids.elements.SelfServiceMetaDataId;
import org.eclipse.winery.repository.datatypes.ids.elements.VisualAppearanceId;
import org.eclipse.winery.repository.export.CSARExporter;
import org.eclipse.winery.repository.ext.imports.custom.DefinitionGenerator;
import org.eclipse.winery.repository.resources.admin.NamespacesResource;
import org.eclipse.winery.repository.resources.servicetemplates.ServiceFilesResource;
import org.eclipse.winery.repository.resources.servicetemplates.ServiceTemplatesResource;
import org.reflections.Reflections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Element;

/**
 * Imports a CSAR into the storage. As the internal storage format does not have CSARs as the
 * topmost artifacts, but one TDefinition, the CSAR has to be split up into several components.
 * 
 * Existing components are <em>not</em> replaced, but silently skipped
 * 
 * Minor errors are logged and not further propagated / notified. That means, a user cannot see
 * minor errors. Major errors are immediately thrown.
 * 
 * One instance for each import
 */
public class CSARImporter {

  private static final Logger logger = LoggerFactory.getLogger(CSARImporter.class);

  // ExecutorService for XSD schema initialization
  // Threads set to 1 to avoid testing for parallel processing of the same XSD file
  private static final ExecutorService xsdParsingService = Executors.newFixedThreadPool(1);

  private static final ExecutorService entityTypeAdjestmentService = Executors
      .newFixedThreadPool(10);

  private static final String YML_FORMAT = ".yml";
  private static final String YAML_FORMAT = ".yaml";

  private TServiceTemplate serviceTemplate = null;

  /**
   * Reads the CSAR from the given inputstream
   * 
   * @param in the inputstream to read from
   * @param errorList the list of errors during the import. Has to be non-null
   * @param overwrite if true: contents of the repo are overwritten
   * 
   * @throws InvalidCSARException if the CSAR is invalid
   */
  public void readCSAR(InputStream in, List<String> errors, boolean overwrite,
      final boolean asyncWPDParsing) throws IOException {
    // we have to extract the file to a temporary directory as
    // the .definitions file does not necessarily have to be the first entry in the archive
    Path csarDir = Files.createTempDirectory("winery");

    try (ZipInputStream zis = new ZipInputStream(in)) {
      ZipEntry entry;
      while ((entry = zis.getNextEntry()) != null) {
        if (!entry.isDirectory()) {
          Path targetPath = csarDir.resolve(entry.getName());
          Files.createDirectories(targetPath.getParent());
          Files.copy(zis, targetPath, StandardCopyOption.REPLACE_EXISTING);
        }
      }

      this.importFromDir(csarDir, errors, overwrite, asyncWPDParsing);
      this.importCustomFile(csarDir);
    } catch (Exception e) {
      CSARImporter.logger.debug("Could not import CSAR", e);
      throw e;
    } finally {
      // cleanup: delete all contents of the temporary directory
      FileUtils.forceDelete(csarDir);
    }
  }

  private void importCustomFile(Path csarDir) throws IOException {
    File[] files = csarDir.toFile().listFiles();
    for (File file : files) {
      if (isNeedImportFile(file)) {
        importSubFiles(csarDir.toFile(), file);
      }
    }
  }

  private boolean isNeedImportFile(File file) {
    if (!file.isDirectory()) {
      return false;
    }

    if (file.getName().equals("Definitions") || file.getName().equals("TOSCA-Metadata")
        || file.getName().equals("xml")) {
      return false;
    }

    return true;
  }

  private void importSubFiles(File rootDir, File subFile) throws IOException {
    File[] files = subFile.listFiles();
    for (File file : files) {
      if (file.isDirectory()) {
        importSubFiles(rootDir, file);
      } else {
        wirteToWineryDb(file.toPath(),
            getRelativePath(rootDir.getAbsolutePath(), subFile.getAbsolutePath()));
      }
    }
  }

  private String getRelativePath(String rootPath, String subPath) {
    return subPath.substring(rootPath.length());
  }

  private void wirteToWineryDb(Path filePath, String relativePath) throws IOException {
    ServiceFilesResource fileResource =
        new ServiceTemplatesResource()
            .getComponentInstaceResource(Util.URLencode(this.serviceTemplate.getTargetNamespace()),
                this.serviceTemplate.getId()).getServiceFilesResource();
    InputStream is = Files.newInputStream(filePath);
    fileResource.importFile(relativePath, filePath.toFile().getName(), is);
  }

  /**
   * Import an extracted CSAR from a directory
   * 
   * @param path the root path of an extracted CSAR file
   * @param overwrite if true: contents of the repo are overwritten
   * @param asyncWPDParsing true if WPD should be parsed asynchronously to speed up the import.
   *        Required, because JUnit terminates the used ExecutorService
   * @throws InvalidCSARException
   * @throws IOException
   */
  void importFromDir(final Path path, final List<String> errors, final boolean overwrite,
      final boolean asyncWPDParsing) throws IOException {
    Path toscaMetaPath = path.resolve("xml/TOSCA-Metadata/TOSCA.meta");
    if (!Files.exists(toscaMetaPath)) {
      toscaMetaPath = path.resolve("TOSCA-Metadata/TOSCA.meta");
    }

    if (!Files.exists(toscaMetaPath)) {
      errors.add("TOSCA.meta does not exist");
      return;
    }
    final TOSCAMetaFileParser tmfp = new TOSCAMetaFileParser();
    final TOSCAMetaFile tmf = tmfp.parse(toscaMetaPath);

    // we do NOT do any sanity checks, of TOSAC.meta
    // and just start parsing

    if (tmf.getEntryDefinitions() != null) {
      // we obey the entry definitions and "just" import that
      // imported definitions are added recursively
      Path defsPath = path.resolve(tmf.getEntryDefinitions());
      this.importDefinitions(tmf, defsPath, errors, overwrite, asyncWPDParsing);

      this.importSelfServiceMetaData(tmf, path, defsPath, errors);
    } else {
      // no explicit entry definitions found
      // we import all available definitions
      // The specification says (cos01, Section 16.1, line 2935) that all definitions are contained
      // in the "Definitions" directory
      // The alternative is to go through all entries in the TOSCA Meta File, but there is no
      // guarantee that this list is complete
      Path definitionsDir = path.resolve("Definitions");
      if (!Files.exists(definitionsDir)) {
        errors.add("No entry definitions defined and Definitions directory does not exist.");
        return;
      }
      final List<IOException> exceptions = new ArrayList<IOException>();
      Files.walkFileTree(definitionsDir, new SimpleFileVisitor<Path>() {

        @Override
        public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
          if (dir.endsWith("Definitions")) {
            return FileVisitResult.CONTINUE;
          } else {
            return FileVisitResult.SKIP_SUBTREE;
          }
        }

        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
          try {
            CSARImporter.this.importDefinitions(tmf, file, errors, overwrite, asyncWPDParsing);
          } catch (IOException e) {
            exceptions.add(e);
            return FileVisitResult.TERMINATE;
          }
          return FileVisitResult.CONTINUE;
        }
      });

      if (!exceptions.isEmpty()) {
        // something went wrong during parsing
        // we rethrow the exception
        throw exceptions.get(0);
      }
    }

    this.importNamespacePrefixes(path);
  }


  private static final Pattern GENERATED_PREFIX_PATTERN = Pattern.compile("^ns\\d+$");


  /**
   * Import namespace prefixes. This is kind of a quick hack. TODO: during the import, the prefixes
   * should be extracted using JAXB and stored in the NamespacesResource
   * 
   * @param rootPath the root path of the extracted CSAR
   */
  private void importNamespacePrefixes(Path rootPath) {
    Path properties = rootPath.resolve(CSARExporter.PATH_TO_NAMESPACES_PROPERTIES);
    if (Files.exists(properties)) {
      PropertiesConfiguration pconf;
      try {
        pconf = new PropertiesConfiguration(properties.toFile());
      } catch (ConfigurationException e) {
        CSARImporter.logger.debug(e.getMessage(), e);
        return;
      }
      Iterator<String> namespaces = pconf.getKeys();
      while (namespaces.hasNext()) {
        boolean addToStorage = false;
        String namespace = namespaces.next();
        if (NamespacesResource.INSTANCE.getIsPrefixKnownForNamespace(namespace)) {
          String storedPrefix = NamespacesResource.getPrefix(namespace);
          // QUICK HACK to check whether the prefix is a generated one
          // We assume we know the internal generation routine
          Matcher m = CSARImporter.GENERATED_PREFIX_PATTERN.matcher(storedPrefix);
          if (m.matches()) {
            // the stored prefix is a generated one
            // replace it by the one stored in the exported properties
            addToStorage = true;
          }
        } else {
          addToStorage = true;
        }
        if (addToStorage) {
          String prefix = pconf.getString(namespace);
          NamespacesResource.INSTANCE.addNamespace(namespace, prefix);
        }
      }
    }
  }

  /**
   * Imports a self-service meta data description (if available)
   * 
   * The first service template in the provided entry definitions is taken
   * 
   * @param tmf
   * 
   * @param errors
   */
  private void importSelfServiceMetaData(final TOSCAMetaFile tmf, final Path rootPath,
      Path entryDefinitions, final List<String> errors) {
    final Path selfServiceDir = rootPath.resolve(Constants.DIRNAME_SELF_SERVICE_METADATA);
    if (!Files.exists(selfServiceDir)) {
      CSARImporter.logger.debug("Self-service Portal directory does not exist in CSAR");
      return;
    }
    if (!Files.exists(entryDefinitions)) {
      CSARImporter.logger.debug("Entry definitions does not exist.");
      return;
    }

    Unmarshaller um = JAXBSupport.createUnmarshaller();
    TDefinitions defs;
    try {
      defs = (TDefinitions) um.unmarshal(entryDefinitions.toFile());
    } catch (JAXBException e) {
      errors.add("Could not unmarshal definitions " + entryDefinitions.getFileName() + " "
          + e.getMessage());
      return;
    } catch (ClassCastException e) {
      errors.add("Definitions " + entryDefinitions.getFileName() + " is not a TDefinitions "
          + e.getMessage());
      return;
    }

    final int cutLength = selfServiceDir.toString().length() + 1;
    Iterator<TExtensibleElements> iterator =
        defs.getServiceTemplateOrNodeTypeOrNodeTypeImplementation().iterator();
    boolean found = false;
    TExtensibleElements next = null;
    while (iterator.hasNext() && !found) {
      next = iterator.next();
      if (next instanceof TServiceTemplate) {
        found = true;
      }
    }

    if (found) {
      TServiceTemplate serviceTemplate = (TServiceTemplate) next;
      String namespace = serviceTemplate.getTargetNamespace();
      if (namespace == null) {
        namespace = defs.getTargetNamespace();
      }
      ServiceTemplateId stId = new ServiceTemplateId(namespace, serviceTemplate.getId(), false);
      final SelfServiceMetaDataId id = new SelfServiceMetaDataId(stId);

      // QUICK HACK: We just import all data without any validation
      // Reason: the metadata resource can deal with nearly arbitrary formats of the data, therefore
      // we do not do any checking here

      try {
        Files.walkFileTree(selfServiceDir, new SimpleFileVisitor<Path>() {

          @Override
          public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
            String name = file.toString().substring(cutLength);
            // check: if name contains "/", this could lead to exceptions
            RepositoryFileReference ref = new RepositoryFileReference(id, name);

            if (name.equals("data.xml")) {
              // we have to check whether the data.xml contains
              // (uri:"http://opentosca.org/self-service", local:"application")
              // instead of
              // (uri:"http://www.eclipse.org/winery/model/selfservice", local:"Application"
              // We quickly replace it via String replacement instead of XSLT
              try {
                String oldContent =
                    org.apache.commons.io.FileUtils.readFileToString(file.toFile(), "UTF-8");
                String newContent =
                    oldContent.replace("http://opentosca.org/self-service",
                        "http://www.eclipse.org/winery/model/selfservice");
                newContent = newContent.replace(":application", ":Application");
                if (!oldContent.equals(newContent)) {
                  // we replaced something -> write new content to old file
                  org.apache.commons.io.FileUtils.writeStringToFile(file.toFile(), newContent,
                      "UTF-8");
                }
              } catch (IOException e) {
                CSARImporter.logger.debug("Could not replace content in data.xml", e);
              }
            }
            CSARImporter.this.importFile(file, ref, tmf, rootPath, errors);
            return FileVisitResult.CONTINUE;
          }
        });
      } catch (IOException e) {
        CSARImporter.logger.debug(e.getMessage(), e);
        errors.add("Self-service Meta Data: " + e.getMessage());
      }
    }

  }

  /**
   * Recursively imports the given definitions
   * 
   * @param tmf the TOSCAMetaFile object holding the parsed content of a TOSCA meta file. If null,
   *        no files must be referenced from the given definitions
   * @param overwrite true: existing contents are overwritten
   * @param asyncWPDParsing
   * @param definitions the path to the definitions to import
   * 
   * @throws IOException
   */
  public void importDefinitions(TOSCAMetaFile tmf, Path defsPath, final List<String> errors,
      boolean overwrite, boolean asyncWPDParsing) throws IOException {
    if (defsPath == null) {
      throw new IllegalStateException("path to definitions must not be null");
    }
    if (!Files.exists(defsPath)) {
      errors.add(String.format("Definitions %1$s does not exist", defsPath.getFileName()));
      return;
    }

    TDefinitions defs;
    defs = createDefinitionsByThirdparty(errors, defsPath);
    if (defs == null) {
      defs = getXMLDefs(errors, defsPath);
    }

    if (defs == null) {
      return;
    }

    List<TImport> imports = defs.getImport();
    this.importImports(defsPath.getParent(), tmf, imports, errors, overwrite, asyncWPDParsing);
    // imports has been modified to contain necessary imports only

    // this method adds new imports to defs which may not be imported using "importImports".
    // Therefore, "importTypes" has to be called *after* importImports
    this.importTypes(defs, errors);

    String defaultNamespace = defs.getTargetNamespace();
    List<TExtensibleElements> componentInstanceList =
        defs.getServiceTemplateOrNodeTypeOrNodeTypeImplementation();
    for (final TExtensibleElements ci : componentInstanceList) {
      // Determine namespace
      String namespace = this.getNamespace(ci, defaultNamespace);
      // Ensure that element has the namespace
      this.setNamespace(ci, namespace);

      // Determine id
      String id = ModelUtilities.getId(ci);
      if (ci instanceof TServiceTemplate) {
        this.serviceTemplate = (TServiceTemplate) ci;
      }

      if (isYamlFormat(defsPath)) {
        new NameSpaceHelper().updateNameSpace(ci);
      }

      // Determine WineryId
      Class<? extends TOSCAComponentId> widClass =
          org.eclipse.winery.repository.Utils.getComponentIdClassForTExtensibleElements(ci
              .getClass());
      final TOSCAComponentId wid = BackendUtils.getTOSCAcomponentId(widClass, namespace, id, false);

      if (Repository.INSTANCE.exists(wid)) {
        if (overwrite) {
          Repository.INSTANCE.forceDelete(wid);
          String msg =
              String.format("Deleted %1$s %2$s to enable replacement", ci.getClass().getName(), wid
                  .getQName().toString());
          CSARImporter.logger.debug(msg);
        } else {
          String msg =
              String.format("Skipped %1$s %2$s, because it already exists",
                  ci.getClass().getName(), wid.getQName().toString());
          CSARImporter.logger.debug(msg);
          // this is not displayed in the UI as we currently do not distinguish between pre-existing
          // types and types created during the import.
          continue;
        }
      }

      // Create a fresh definitions object without the other data.
      final Definitions newDefs = BackendUtils.createWrapperDefinitions(wid);

      // copy over the inputs determined by this.importImports
      newDefs.getImport().addAll(imports);

      // add the current TExtensibleElements as the only content to it
      newDefs.getServiceTemplateOrNodeTypeOrNodeTypeImplementation().add(ci);

      adjustRefFiles(tmf, defsPath, errors, ci, wid);

      // node types and relationship types are subclasses of TEntityType
      // Therefore, we check the entity type separately here
      if (ci instanceof TEntityType) {
        if (asyncWPDParsing) {
          // Adjusting takes a long time
          // Therefore, we first save the type as is and convert to Winery-Property-Definitions in
          // the background
          CSARImporter.storeDefinitions(wid, newDefs);
          CSARImporter.entityTypeAdjestmentService.submit(new Runnable() {

            @Override
            public void run() {
              CSARImporter.adjustEntityType((TEntityType) ci, (EntityTypeId) wid, newDefs, errors);
              CSARImporter.storeDefinitions(wid, newDefs);
            }
          });
        } else {
          CSARImporter.adjustEntityType((TEntityType) ci, (EntityTypeId) wid, newDefs, errors);
          CSARImporter.storeDefinitions(wid, newDefs);
        }
      } else {
        CSARImporter.storeDefinitions(wid, newDefs);
      }
    }
  }


  private void adjustRefFiles(TOSCAMetaFile tmf, Path defsPath, final List<String> errors,
      final TExtensibleElements ci, final TOSCAComponentId wid) throws IOException {
    if (ci instanceof TArtifactTemplate) {
      // convention: Definitions are stored in the "Definitions" directory, therefore going to
      // levels up (Definitions dir -> root dir) resolves to the root dir
      // COS01, line 2663 states that the path has to be resolved from the *root* of the CSAR
      this.adjustArtifactTemplate(defsPath.getParent().getParent(), tmf, (ArtifactTemplateId) wid,
          (TArtifactTemplate) ci, errors);
    } else if (ci instanceof TNodeType) {
      this.adjustNodeType(defsPath.getParent().getParent(), (TNodeType) ci, (NodeTypeId) wid, tmf,
          errors);
    } else if (ci instanceof TRelationshipType) {
      this.adjustRelationshipType(defsPath.getParent().getParent(), (TRelationshipType) ci,
          (RelationshipTypeId) wid, tmf, errors);
    } else if (ci instanceof TServiceTemplate) {
      this.adjustServiceTemplate(defsPath.getParent().getParent(), tmf, (ServiceTemplateId) wid,
          (TServiceTemplate) ci, errors);
    }
  }

  private boolean isYamlFormat(Path path) {
    return (path.toFile().getName().indexOf(YML_FORMAT) != -1)
        || (path.toFile().getName().indexOf(YAML_FORMAT) != -1);
  }

  private TDefinitions getXMLDefs(final List<String> errors, Path defsPath) {
    TDefinitions defs = null;
    try {
      Unmarshaller um = JAXBSupport.createUnmarshaller();
      defs = (TDefinitions) um.unmarshal(defsPath.toFile());
    } catch (JAXBException e) {
      Throwable cause = e;
      String eMsg = "";
      do {
        String msg = cause.getMessage();
        if (msg != null) {
          eMsg = eMsg + msg + "; ";
        }
        cause = cause.getCause();
      } while (cause != null);
      errors.add("Could not unmarshal definitions " + defsPath.getFileName() + " " + eMsg);
      CSARImporter.logger.debug("Unmarshalling error", e);
    } catch (ClassCastException e) {
      errors.add("Definitions " + defsPath.getFileName() + " is not a TDefinitions "
          + e.getMessage());
    }
    return defs;
  }

  private TDefinitions createDefinitionsByThirdparty(final List<String> errors, Path defsPath) {
    TDefinitions defs = null;
    try {
      Reflections reflections = new Reflections("org.eclipse.winery.repository.ext");
      Set<Class<? extends DefinitionGenerator>> implenmetions =
          reflections
              .getSubTypesOf(org.eclipse.winery.repository.ext.imports.custom.DefinitionGenerator.class);
      Iterator<Class<? extends DefinitionGenerator>> it = implenmetions.iterator();
      while (it.hasNext()) {
        Class<? extends DefinitionGenerator> impl = it.next();
        if (!Modifier.isAbstract(impl.getModifiers())) {
          DefinitionGenerator generator = impl.newInstance();
          if (generator.accept(defsPath)) {
            try {
              defs = generator.makeDefinitions(defsPath);
            } catch (Exception e) {
              CSARImporter.logger
                  .error("error occurs while make defintions by: " + impl.toString());
              continue;
            }
          } else
            CSARImporter.logger.info(impl.toString() + " refuse to deal with file: "
                + defsPath.getFileName());
        }
      }

    } catch (Exception e) {
      errors.add("Could not parse definitions " + defsPath.getFileName() + " ");
      CSARImporter.logger.debug("parse error", e);
    }
    return defs;
  }

  /**
   * Imports the specified types into the repository. The types are converted to an import statement
   * 
   * @param errors Container for error messages
   */
  private void importTypes(TDefinitions defs, final List<String> errors) {
    Types typesContainer = defs.getTypes();
    if (typesContainer != null) {
      List<Object> types = typesContainer.getAny();
      for (Object type : types) {
        if (type instanceof Element) {
          Element element = (Element) type;

          // generate id part of ImportId out of definitions' id
          // we do not use the name as the name has to be URLencoded again and we have issues with
          // the interplay with
          // org.eclipse.winery.common.ids.definitions.imports.GenericImportId.getId(TImport) then.
          String id = defs.getId();
          // try to make the id unique by hashing the "content" of the definition
          id = id + "-" + Integer.toHexString(element.hashCode());

          // set importId
          TOSCAComponentId importId;
          String ns;
          if (element.getNamespaceURI().equals(XMLConstants.W3C_XML_SCHEMA_NS_URI)) {
            ns = element.getAttribute("targetNamespace");
            importId = new XSDImportId(ns, id, false);
          } else {
            // Quick hack for non-XML-Schema-definitions
            ns = "unknown";
            importId = new GenericImportId(ns, id, false, element.getNamespaceURI());
          }

          // Following code is adapted from importOtherImports

          TDefinitions wrapperDefs = BackendUtils.createWrapperDefinitions(importId);
          TImport imp = new TImport();
          String fileName = id + ".xsd";
          imp.setLocation(fileName);
          imp.setImportType(XMLConstants.W3C_XML_SCHEMA_NS_URI);
          imp.setNamespace(ns);
          wrapperDefs.getImport().add(imp);
          CSARImporter.storeDefinitions(importId, wrapperDefs);

          // put the file itself to the repo
          // ref is required to generate fileRef
          RepositoryFileReference ref = BackendUtils.getRefOfDefinitions(importId);
          RepositoryFileReference fileRef = new RepositoryFileReference(ref.getParent(), fileName);
          // convert element to document
          // QUICK HACK. Alternative: Add new method Repository.INSTANCE.getOutputStream and
          // transform DOM node to OuptputStream
          String content = Util.getXMLAsString(element);
          try {
            Repository.INSTANCE.putContentToFile(fileRef, content, MediaType.APPLICATION_XML_TYPE);
          } catch (IOException e) {
            CSARImporter.logger.debug(
                "Could not put XML Schema definition to file " + fileRef.toString(), e);
            errors.add("Could not put XML Schema definition to file " + fileRef.toString());
          }

          // add import to definitions

          // adapt path - similar to importOtherImport
          String newLoc =
              "../" + Utils.getURLforPathInsideRepo(BackendUtils.getPathInsideRepo(fileRef));
          imp.setLocation(newLoc);
          defs.getImport().add(imp);
        } else {
          // This is a known type. Otherwise JAX-B would render it as Element
          errors.add("There is a Type of class " + type.getClass().toString()
              + " which is unknown to Winery. The type element is imported as is");
        }
      }
    }
  }

  /**
   * All EntityTypes may contain properties definition. In case a winery properties definition is
   * found, the TOSCA conforming properties definition is removed
   * 
   * @param ci the entity type
   * @param wid the Winery id of the entitytype
   * @param newDefs the definitions, the entiy type is contained in. The imports might be adjusted
   *        here
   * @param errors
   */
  private static void adjustEntityType(TEntityType ci, EntityTypeId wid, Definitions newDefs,
      final List<String> errors) {
    adjustEntityTypeProperties(ci, wid, newDefs, errors);
  }

  private static void adjustEntityTypeProperties(TEntityType ci, EntityTypeId wid,
      Definitions newDefs, final List<String> errors) {
    PropertiesDefinition propertiesDefinition = ci.getPropertiesDefinition();
    if (propertiesDefinition != null) {
      WinerysPropertiesDefinition winerysPropertiesDefinition =
          ModelUtilities.getWinerysPropertiesDefinition(ci);
      boolean deriveWPD;
      if (winerysPropertiesDefinition == null) {
        deriveWPD = true;
      } else {
        if (winerysPropertiesDefinition.getIsDerivedFromXSD() == null) {
          // if the winery's properties are defined by Winery itself,
          // remove the TOSCA conforming properties definition as a Winery properties definition
          // exists (and which takes precedence)
          ci.setPropertiesDefinition(null);

          // no derivation from properties required as the properties are generated by Winery
          deriveWPD = false;

          // we have to remove the import, too
          // Determine the location
          String elementName = winerysPropertiesDefinition.getElementName();
          String loc =
              BackendUtils.getImportLocationForWinerysPropertiesDefinitionXSD(wid, null,
                  elementName);
          // remove the import matching that location
          List<TImport> imports = newDefs.getImport();
          boolean found = false;
          if (imports != null) {
            Iterator<TImport> iterator = imports.iterator();
            TImport imp;
            while (iterator.hasNext()) {
              imp = iterator.next();
              // TODO: add check for QNames.QNAME_WINERYS_PROPERTIES_DEFINITION_ATTRIBUTE instead of
              // import location. The current routine, however, works, too.
              if (imp.getLocation().equals(loc)) {
                found = true;
                break;
              }
            }
            if (found) {
              // imp with Winery's k/v location found
              iterator.remove();
              // the XSD has been imported in importOtherImport
              // it was too difficult to do the location check there, therefore we just remove the
              // XSD from the repository here
              XSDImportId importId =
                  new XSDImportId(winerysPropertiesDefinition.getNamespace(), elementName, false);
              try {
                Repository.INSTANCE.forceDelete(importId);
              } catch (IOException e) {
                CSARImporter.logger.debug("Could not delete Winery's generated XSD definition", e);
                errors.add("Could not delete Winery's generated XSD definition");
              }
            } else {
              // K/V properties definition was incomplete
            }
          }
        } else {
          // winery's properties are derived from an XSD
          // The export does NOT add an imports statement: only the wpd exists
          // We remove that as
          ModelUtilities.removeWinerysPropertiesDefinition(ci);
          // derive the WPDs again from the properties definition
          deriveWPD = true;
        }
      }
      if (deriveWPD) {
        BackendUtils.deriveWPD(ci, errors);
      }
    }
  }

  /**
   * In case plans are provided, the plans are imported into Winery's storage
   * 
   * @param rootPath the root path of the extracted csar
   * @param tmf the TOSCAMetaFile object used to determine the mime type of the plan
   * @param wid Winery's internal id of the service template
   * @param st the the service template to be imported {@inheritDoc}
   * 
   * @throws InvalidCSARException
   */
  private void adjustServiceTemplate(Path rootPath, TOSCAMetaFile tmf, ServiceTemplateId wid,
      TServiceTemplate st, final List<String> errors) {
    TPlans plans = st.getPlans();
    if (plans != null) {
      for (TPlan plan : plans.getPlan()) {
        PlanModelReference refContainer = plan.getPlanModelReference();
        if (refContainer != null) {
          String ref = refContainer.getReference();
          if (ref != null) {
            // URLs are stored encoded -> undo the encoding
            ref = Util.URLdecode(ref);
            URI refURI;
            try {
              refURI = new URI(ref);
            } catch (URISyntaxException e) {
              errors.add(String.format("Invalid URI %1$s", ref));
              continue;
            }
            if (refURI.isAbsolute()) {
              // Points to somewhere external
              // This is a linked plan
              // We have to do nothing
              continue;
            }
            Path path = rootPath.resolve(ref);
            if (!Files.exists(path)) {
              // possibly, the reference is relative to the Definitions subfolder
              // COS01 does not make any explicit statement how to resolve the reference here
              path = rootPath.resolve("Definitions").resolve(ref);
              if (!Files.exists(path)) {
                errors.add(String.format("Plan reference %1$s not found", ref));
                // we quickly remove the reference to reflect the not-found in the data
                refContainer.setReference(null);
                continue;
              }
            }
            PlansId plansId = new PlansId(wid);
            PlanId pid = new PlanId(plansId, new XMLId(plan.getId(), false));
            if (Files.isDirectory(path)) {
              errors
                  .add(String
                      .format(
                          "Reference %1$s is a directory and Winery currently does not support importing directories",
                          ref));
              continue;
            }
            RepositoryFileReference fref =
                new RepositoryFileReference(pid, path.getFileName().toString());
            this.importFile(path, fref, tmf, rootPath, errors);

            // file is imported
            // Adjust the reference
            refContainer.setReference("../"
                + Utils.getURLforPathInsideRepo(BackendUtils.getPathInsideRepo(fref)));
          }
        }
      }
    }
  }

  /**
   * Adds a color to the given relationship type
   */
  private void adjustRelationshipType(Path rootPath, TRelationshipType ci, RelationshipTypeId wid,
      TOSCAMetaFile tmf, final List<String> errors) {
    VisualAppearanceId visId = new VisualAppearanceId(wid);
    this.importIcons(rootPath, visId, tmf, errors);
  }

  private void adjustNodeType(Path rootPath, TNodeType ci, NodeTypeId wid, TOSCAMetaFile tmf,
      final List<String> errors) {
    VisualAppearanceId visId = new VisualAppearanceId(wid);
    this.importIcons(rootPath, visId, tmf, errors);
  }

  private void importIcons(Path rootPath, VisualAppearanceId visId, TOSCAMetaFile tmf,
      final List<String> errors) {
    String pathInsideRepo = BackendUtils.getPathInsideRepo(visId);
    Path visPath = rootPath.resolve(pathInsideRepo);
    this.importIcon(visId, visPath, Filename.FILENAME_BIG_ICON, tmf, rootPath, errors);
  }

  private void importIcon(VisualAppearanceId visId, Path visPath, String fileName,
      TOSCAMetaFile tmf, Path rootPath, final List<String> errors) {
    Path file = visPath.resolve(fileName);
    if (Files.exists(file)) {
      RepositoryFileReference ref = new RepositoryFileReference(visId, fileName);
      this.importFile(file, ref, tmf, rootPath, errors);
    }
  }

  /**
   * Adjusts the given artifact template to conform with the repository format
   * 
   * We import the files given at the artifact references
   * 
   * @throws InvalidCSARException
   * @throws IOException
   */
  private void adjustArtifactTemplate(Path rootPath, TOSCAMetaFile tmf, ArtifactTemplateId atid,
      TArtifactTemplate ci, final List<String> errors) throws IOException {
    ArtifactReferences refs = ci.getArtifactReferences();
    if (refs == null) {
      // no references stored - break
      return;
    }
    List<TArtifactReference> refList = refs.getArtifactReference();
    Iterator<TArtifactReference> iterator = refList.iterator();
    while (iterator.hasNext()) {
      TArtifactReference ref = iterator.next();
      String reference = ref.getReference();
      // URLs are stored encoded -> undo the encoding
      reference = Util.URLdecode(reference);

      URI refURI;
      try {
        refURI = new URI(reference);
      } catch (URISyntaxException e) {
        errors.add(String.format("Invalid URI %1$s", ref));
        continue;
      }
      if (refURI.isAbsolute()) {
        // Points to somewhere external
        // We have to do nothing
        continue;
      }

      // we remove the current element as it will be handled during the export
      iterator.remove();

      Path path = rootPath.resolve(reference);
      if (!Files.exists(path)) {
        errors.add(String.format("Reference %1$s not found", reference));
        return;
      }
      Set<Path> allFiles;
      if (Files.isRegularFile(path)) {
        allFiles = new HashSet<Path>();
        allFiles.add(path);
      } else {
        assert (Files.isDirectory(path));
        Path localRoot = rootPath.resolve(path);
        List<Object> includeOrExclude = ref.getIncludeOrExclude();

        if (includeOrExclude.get(0) instanceof TArtifactReference.Exclude) {
          // Implicit semantics of an exclude listed first:
          // include all files and then exclude the files matched by the pattern
          allFiles = this.getAllFiles(localRoot);
        } else {
          // semantics if include lited as first:
          // same as listed at other places
          allFiles = new HashSet<>();
        }

        for (Object object : includeOrExclude) {
          if (object instanceof TArtifactReference.Include) {
            this.handleInclude((TArtifactReference.Include) object, localRoot, allFiles);
          } else {
            assert (object instanceof TArtifactReference.Exclude);
            this.handleExclude((TArtifactReference.Exclude) object, localRoot, allFiles);
          }
        }
      }
      this.importAllFiles(allFiles, atid, tmf, rootPath, errors);
    }

    if (refList.isEmpty()) {
      // everything is imported and is a file stored locally
      // we don't need the references stored locally: they are generated on the fly when exporting
      ci.setArtifactReferences(null);
    }
  }

  /**
   * Imports a file from the filesystem to the repository
   * 
   * @param p the file to read from
   * @param fref the "file" to put the content to
   * @param tmf the TOSCAMetaFile object used to determine the mimetype. Must not be null.
   * @param rootPath used to relativize p to determine the mime type
   * @throws InvalidCSARException
   */
  private void importFile(Path p, RepositoryFileReference fref, TOSCAMetaFile tmf, Path rootPath,
      final List<String> errors) {
    if (tmf == null) {
      throw new IllegalStateException("tmf must not be null");
    }
    try (InputStream is = Files.newInputStream(p);
        BufferedInputStream bis = new BufferedInputStream(is)) {
      String mediaType = tmf.getMimeType(p.relativize(rootPath).toString());
      if (mediaType == null) {
        // Manually find out mime type
        try {
          mediaType = Utils.getMimeType(bis, p.getFileName().toString());
        } catch (IOException e) {
          errors.add(String.format("No MimeType given for %1$s (%2$s)", p.getFileName(),
              e.getMessage()));
          return;
        }
        if (mediaType == null) {
          errors.add(String.format("No MimeType given for %1$s", p.getFileName()));
          return;
        }
      }
      try {
        Repository.INSTANCE.putContentToFile(fref, bis, MediaType.valueOf(mediaType));
      } catch (IllegalArgumentException | IOException e) {
        throw new IllegalStateException(e);
      }
    } catch (IOException e1) {
      throw new IllegalStateException("Could not work on generated temporary files", e1);
    }
  }

  private void importAllFiles(Collection<Path> allFiles, ArtifactTemplateId atid,
      TOSCAMetaFile tmf, Path rootPath, final List<String> errors) {
    // import all files to repository
    ArtifactTemplateDirectoryId fileDir = new ArtifactTemplateDirectoryId(atid);
    for (Path p : allFiles) {
      if (!Files.exists(p)) {
        errors.add(String.format("File %1$s does not exist", p.toString()));
        return;
      }
      RepositoryFileReference fref =
          new RepositoryFileReference(fileDir, p.getFileName().toString());
      this.importFile(p, fref, tmf, rootPath, errors);
    }

  }

  /**
   * Modifies given allFiles object to exclude all files given by the excl pattern
   * 
   * Semantics: Remove all files from the set, which match the given pattern
   */
  private void handleExclude(Exclude excl, Path localRoot, Set<Path> allFiles) {
    PathMatcher pathMatcher = localRoot.getFileSystem().getPathMatcher("glob:" + excl.getPattern());
    Iterator<Path> it = allFiles.iterator();
    while (it.hasNext()) {
      Path curPath = it.next();
      if (pathMatcher.matches(curPath)) {
        it.remove();
      }
    }
  }

  /**
   * Modifies given allFiles object to include all files given by the incl pattern
   * 
   * Semantics: Add all files from localRoot to allFiles matching the pattern
   */
  private void handleInclude(final Include incl, final Path localRoot, final Set<Path> allFiles) {
    final PathMatcher pathMatcher =
        localRoot.getFileSystem().getPathMatcher("glob:" + incl.getPattern());
    try {
      Files.walkFileTree(localRoot, new SimpleFileVisitor<Path>() {

        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
          Path relFile = localRoot.relativize(file);
          if (pathMatcher.matches(relFile)) {
            allFiles.add(file);
          }
          return CONTINUE;
        }

        @Override
        public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
            throws IOException {
          if (pathMatcher.matches(dir)) {
            Set<Path> filesToAdd = CSARImporter.this.getAllFiles(dir);
            allFiles.addAll(filesToAdd);
            return SKIP_SUBTREE;
          } else {
            return CONTINUE;
          }
        }
      });
    } catch (IOException e) {
      throw new IllegalStateException(e);
    }
  }

  /**
   * Lists all files contained in the given path
   */
  private Set<Path> getAllFiles(Path startPath) {
    final Set<Path> res = new HashSet<>();
    try {
      Files.walkFileTree(startPath, new SimpleFileVisitor<Path>() {

        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
          res.add(file);
          return CONTINUE;
        }
      });
    } catch (IOException e) {
      throw new IllegalStateException(e);
    }
    return res;
  }

  /**
   * Sets the namespace on the CI if CI offers the method "setTargetNamespace"
   * 
   * @param ci the component instance to set the namespace
   * @param namespace the namespace to set
   */
  private void setNamespace(TExtensibleElements ci, String namespace) {
    Method method;
    try {
      method = ci.getClass().getMethod("setTargetNamespace", String.class);
      method.invoke(ci, namespace);
    } catch (NoSuchMethodException ne) {
      // this is OK, because we do not check, whether the method really exists
      // Special case for TArtifactTemplate not offering setTargetNamespace
      // just ignore it
    } catch (Exception e) {
      throw new IllegalStateException("Could not set target namespace", e);
    }
  }

  /**
   * @param ci the component instance to get the namespace from
   * @param defaultNamespace the namespace to use if the TExtensibleElements has no targetNamespace
   */
  private String getNamespace(TExtensibleElements ci, String defaultNamespace) {
    Method method;
    Object res;
    try {
      method = ci.getClass().getMethod("getTargetNamespace");
      res = method.invoke(ci);
    } catch (Exception e) {
      // we are at TArtifactTemplate, which does not offer getTargetNamespace
      res = null;
    }
    String ns = (String) res;
    if (ns == null) {
      ns = defaultNamespace;
    }
    return ns;
  }

  /**
   * @param basePath the base path where to resolve files from. This is the directory of the
   *        Definitions
   * @param imports the list of imports to import. SIDE EFFECT: this list is modified. After this
   *        method has run, the list contains the imports to be put into the wrapper element
   */
  private void importImports(Path basePath, TOSCAMetaFile tmf, List<TImport> imports,
      final List<String> errors, boolean overwrite, final boolean asyncWPDParsing)
      throws IOException {
    for (Iterator<TImport> iterator = imports.iterator(); iterator.hasNext();) {
      TImport imp = iterator.next();
      String importType = imp.getImportType();
      String namespace = imp.getNamespace();
      String loc = imp.getLocation();

      if (namespace == null) {
        errors.add("not namespace-qualified imports are not supported.");
        continue;
      }

      if (loc == null) {
        errors.add("Empty location imports are not supported.");
      } else {
        if (importType.equals(Namespaces.TOSCA_NAMESPACE)) {
          if (!Util.isRelativeURI(loc)) {
            errors.add("Absolute URIs for definitions import not supported.");
            continue;
          }

          // URIs are encoded
          loc = Util.URLdecode(loc);

          Path defsPath = basePath.resolve(loc);
          // fallback for older CSARs, where the location is given from the root
          if (!Files.exists(defsPath)) {
            defsPath = basePath.getParent().resolve(loc);
            // the real existence check is done in importDefinitions
          }
          this.importDefinitions(tmf, defsPath, errors, overwrite, asyncWPDParsing);
          // imports of definitions don't have to be kept as these are managed by Winery
          iterator.remove();
        } else {
          this.importOtherImport(basePath, imp, errors, importType, overwrite);
        }
      }
    }
  }

  /**
   * SIDE EFFECT: modifies the location of imp to point to the correct relative location (when read
   * from the exported CSAR)
   * 
   * @param rootPath the absolute path where to resolve files from
   */
  private void importOtherImport(Path rootPath, TImport imp, final List<String> errors,
      String type, boolean overwrite) {
    assert (!type.equals(Namespaces.TOSCA_NAMESPACE));
    String loc = imp.getLocation();

    if (!Util.isRelativeURI(loc)) {
      // This is just an information message
      errors.add("Absolute URIs are not resolved by Winery (" + loc + ")");
      return;
    }

    // location URLs are encoded: http://www.w3.org/TR/2001/WD-charmod-20010126/#sec-URIs, RFC
    // http://www.ietf.org/rfc/rfc2396.txt
    loc = Util.URLdecode(loc);
    Path path;
    try {
      path = rootPath.resolve(loc);
    } catch (Exception e) {
      // java.nio.file.InvalidPathException could be thrown which is a RuntimeException
      errors.add(e.getMessage());
      return;
    }
    if (!Files.exists(path)) {
      // fallback for older CSARs, where the location is given from the root
      path = rootPath.getParent().resolve(loc);
      if (!Files.exists(path)) {
        errors.add(String.format("File %1$s does not exist", loc));
        return;
      }
    }
    String namespace = imp.getNamespace();
    String fileName = path.getFileName().toString();
    String id = fileName;
    id = FilenameUtils.removeExtension(id);
    // Convention: id of import is filename without extension

    GenericImportId rid;
    if (type.equals(XMLConstants.W3C_XML_SCHEMA_NS_URI)) {
      rid = new XSDImportId(namespace, id, false);
    } else {
      rid = new GenericImportId(namespace, id, false, type);
    }

    boolean importDataExistsInRepo = Repository.INSTANCE.exists(rid);

    if (!importDataExistsInRepo) {
      // We have to
      // a) create a .definitions file
      // b) put the file itself in the repo

      // Create the definitions file
      TDefinitions defs = BackendUtils.createWrapperDefinitions(rid);
      defs.getImport().add(imp);
      // QUICK HACK: We change the imp object's location here and below again
      // This is "OK" as "storeDefinitions" serializes the current state and not the future state of
      // the imp object
      // change the location to point to the file in the folder of the .definitions file
      imp.setLocation(fileName);

      // put the definitions file to the repository
      CSARImporter.storeDefinitions(rid, defs);
    }

    // put the file itself to the repo
    // ref is required to generate fileRef
    RepositoryFileReference ref = BackendUtils.getRefOfDefinitions(rid);
    RepositoryFileReference fileRef = new RepositoryFileReference(ref.getParent(), fileName);

    // location is relative to Definitions/
    // even if the import already exists, we have to adapt the path
    // URIs are encoded
    String newLoc = "../" + Utils.getURLforPathInsideRepo(BackendUtils.getPathInsideRepo(fileRef));
    imp.setLocation(newLoc);

    if (!importDataExistsInRepo || overwrite) {
      // finally write the file to the storage
      try (InputStream is = Files.newInputStream(path);
          BufferedInputStream bis = new BufferedInputStream(is)) {
        MediaType mediaType;
        if (type.equals(XMLConstants.W3C_XML_SCHEMA_NS_URI)) {
          mediaType = MediaType.valueOf(MimeTypes.MIMETYPE_XSD);
        } else {
          String mimeType = Utils.getMimeType(bis, path.getFileName().toString());
          mediaType = MediaType.valueOf(mimeType);
        }
        Repository.INSTANCE.putContentToFile(fileRef, bis, mediaType);
      } catch (IllegalArgumentException | IOException e) {
        throw new IllegalStateException(e);
      }

      // we have to update the cache in case of a new XSD to speedup usage of winery
      if (rid instanceof XSDImportId) {
        // We do the initialization asynchronously
        // We do not check whether the XSD has already been checked
        // We cannot just checck whether an XSD already has been handled since the XSD could change
        // over time
        // Synchronization at
        // org.eclipse.winery.repository.resources.imports.xsdimports.XSDImportResource.getAllDefinedLocalNames(short)
        // also isn't feasible as the backend doesn't support locks
        CSARImporter.xsdParsingService.submit(new Runnable() {

          @Override
          public void run() {
            CSARImporter.logger.debug("Updating XSD import cache data");
            // We call the queries without storing the result:
            // We use the SIDEEFFECT that a cache is created
            Utils.getAllXSDElementDefinitionsForTypeAheadSelection();
            Utils.getAllXSDTypeDefinitionsForTypeAheadSelection();
            CSARImporter.logger.debug("Updated XSD import cache data");
          }
        });
      }
    }
  }

  private static void storeDefinitions(TOSCAComponentId id, TDefinitions defs) {
    RepositoryFileReference ref = BackendUtils.getRefOfDefinitions(id);
    String s = Utils.getXMLAsString(defs, true);
    try {
      Repository.INSTANCE.putContentToFile(ref, s,
          MediaType.valueOf(MimeTypes.MIMETYPE_TOSCA_DEFINITIONS));
    } catch (IllegalArgumentException | IOException e) {
      throw new IllegalStateException(e);
    }
  }
}
